package com.warom.sdg.util;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.ghgande.j2mod.modbus.ModbusException;
import com.ghgande.j2mod.modbus.io.ModbusSerialTransaction;
import com.ghgande.j2mod.modbus.msg.ModbusRequest;
import com.ghgande.j2mod.modbus.msg.ModbusResponse;
import com.ghgande.j2mod.modbus.msg.ReadCoilsRequest;
import com.ghgande.j2mod.modbus.msg.ReadCoilsResponse;
import com.ghgande.j2mod.modbus.msg.ReadInputDiscretesRequest;
import com.ghgande.j2mod.modbus.msg.ReadInputDiscretesResponse;
import com.ghgande.j2mod.modbus.msg.ReadInputRegistersRequest;
import com.ghgande.j2mod.modbus.msg.ReadInputRegistersResponse;
import com.ghgande.j2mod.modbus.msg.ReadMultipleRegistersRequest;
import com.ghgande.j2mod.modbus.msg.ReadMultipleRegistersResponse;
import com.ghgande.j2mod.modbus.msg.WriteCoilRequest;
import com.ghgande.j2mod.modbus.msg.WriteMultipleCoilsRequest;
import com.ghgande.j2mod.modbus.msg.WriteMultipleRegistersRequest;
import com.ghgande.j2mod.modbus.msg.WriteSingleRegisterRequest;
import com.ghgande.j2mod.modbus.net.SerialConnection;
import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
import com.ghgande.j2mod.modbus.util.BitVector;
import com.ghgande.j2mod.modbus.Modbus;

import com.warom.sdg.callback.PlcDataListener;

import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Modbus RTU 客户端工具类
 * 使用j2mod库实现Modbus RTU协议
 */
public class ModbusRtuClient {
    private static final String TAG = "ModbusRtuClient";
    
    // 串口相关变量
    private SerialConnection connection;
    private ModbusSerialTransaction transaction;
    private SerialParameters parameters;
    
    // 连接配置
    private String serialPortPath;
    private int baudRate;
    private int dataBits = 8;
    private int stopBits = 1;
    private int parity = 0; // 0: NONE, 1: ODD, 2: EVEN
    private int timeout = 1000; // 默认超时1秒
    private String encoding = Modbus.DEFAULT_SERIAL_ENCODING;
    private boolean echo = false;
    
    // 连接状态
    private boolean connected = false;
    private int unitId = 1; // 默认单元ID
    
    // 断线重连相关
    private static final int DEFAULT_RECONNECT_INTERVAL = 5000; // 默认重连间隔5秒
    private int reconnectInterval = DEFAULT_RECONNECT_INTERVAL;
    private int maxReconnectAttempts = 3; // 最大重连尝试次数
    private int reconnectAttempts = 0;
    private Handler reconnectHandler;
    private AtomicBoolean isReconnecting = new AtomicBoolean(false);
    private boolean autoReconnect = false;
    
    // 数据回调
    private PlcDataListener dataListener;
    
    /**
     * 构造函数
     * @param serialPortPath 串口路径
     * @param baudRate 波特率
     * @param listener 数据监听器
     */
    public ModbusRtuClient(String serialPortPath, int baudRate, PlcDataListener listener) {
        this.serialPortPath = serialPortPath;
        this.baudRate = baudRate;
        this.dataListener = listener;
        reconnectHandler = new Handler(Looper.getMainLooper());
        
        // 初始化串口参数
        this.parameters = new SerialParameters(serialPortPath, baudRate);
        this.parameters.setDatabits(dataBits);
        this.parameters.setStopbits(stopBits);
        this.parameters.setParity(parity);
        this.parameters.setReceiveTimeout(timeout);
        this.parameters.setEncoding(encoding);
        this.parameters.setEcho(echo);
    }
    
    /**
     * 设置串口参数
     * @param dataBits 数据位 (5-8)
     * @param stopBits 停止位 (1-2)
     * @param parity 校验位 (0:无校验, 1:奇校验, 2:偶校验)
     */
    public void setSerialPortParams(int dataBits, int stopBits, int parity) {
        this.dataBits = dataBits;
        this.stopBits = stopBits;
        this.parity = parity;
        
        // 更新参数对象
        this.parameters.setDatabits(dataBits);
        this.parameters.setStopbits(stopBits);
        this.parameters.setParity(parity);
    }
    
    /**
     * 设置超时时间
     * @param timeout 超时时间（毫秒）
     */
    public void setTimeout(int timeout) {
        this.timeout = timeout;
        this.parameters.setReceiveTimeout(timeout);
        
        if (connection != null) {
            connection.setTimeout(timeout);
        }
    }
    
    /**
     * 设置Modbus设备ID
     * @param unitId 设备ID (1-247)
     */
    public void setUnitId(int unitId) {
        if (unitId < 1 || unitId > 247) {
            throw new IllegalArgumentException("设备ID必须在1-247范围内");
        }
        this.unitId = unitId;
    }
    
    /**
     * 连接到串口设备
     * @return 连接是否成功
     */
    public boolean connect() {
        if (connected) {
            Log.d(TAG, "已经连接到串口: " + serialPortPath);
            return true;
        }
        
        try {
            Log.d(TAG, "尝试连接到串口: " + serialPortPath + ", 波特率: " + baudRate);
            
            // 确保J2mod库的兼容性修复已初始化
            J2modFix.init();
            
            // 创建串口连接
            connection = new SerialConnection(parameters);
            
            // 设置超时时间
            connection.setTimeout(parameters.getReceiveTimeout());
            
            // 打开串口连接
            connection.open();
            
            // 创建Modbus事务
            transaction = new ModbusSerialTransaction(connection);
            transaction.setRetries(1);
            
            connected = true;
            reconnectAttempts = 0;
            
            Log.d(TAG, "成功连接到串口: " + serialPortPath);
            return true;
        } catch (Exception e) {
            Log.e(TAG, "连接串口失败: " + e.getMessage(), e);
            disconnect();
            
            if (dataListener != null) {
                dataListener.onError("连接串口失败: " + e.getMessage());
            }
            
            return false;
        }
    }
    
    /**
     * 断开与串口设备的连接
     */
    public void disconnect() {
        connected = false;
        
        // 关闭串口连接
        if (connection != null) {
            try {
                connection.close();
            } catch (Exception e) {
                Log.e(TAG, "关闭串口连接失败: " + e.getMessage(), e);
            }
            connection = null;
        }
        
        transaction = null;
        
        Log.d(TAG, "已断开串口连接: " + serialPortPath);
    }
    
    /**
     * 检查连接状态
     * @return 是否已连接
     */
    public boolean isConnected() {
        return connected && connection != null && connection.isOpen();
    }
    
    /**
     * 读取线圈(Coil)状态
     * @param address 起始地址 (0-65535)
     * @param count 读取数量 (1-2000)
     */
    public void readCoils(final int address, final int count) {
        if (!isConnected()) {
            if (dataListener != null) {
                dataListener.onError("未连接到串口设备");
            }
            return;
        }
        
        // 创建后台线程发送请求
        new Thread(() -> {
            try {
                // 创建读取线圈请求
                ReadCoilsRequest request = new ReadCoilsRequest(address, count);
                request.setUnitID(unitId);
                
                // 执行请求
                transaction.setRequest(request);
                transaction.execute();
                
                // 获取响应
                ReadCoilsResponse response = (ReadCoilsResponse) transaction.getResponse();
                
                // 解析结果
                BitVector bitVector = response.getCoils();
                boolean[] values = new boolean[count];
                for (int i = 0; i < count; i++) {
                    values[i] = bitVector.getBit(i);
                }
                
                // 在主线程通知结果
                final boolean[] finalValues = values;
                new Handler(Looper.getMainLooper()).post(() -> {
                    if (dataListener != null) {
                        dataListener.onDataReceived(finalValues, "coil", String.valueOf(address));
                    }
                });
            } catch (Exception e) {
                Log.e(TAG, "读取线圈失败: " + e.getMessage(), e);
                final String errorMsg = e.getMessage();
                new Handler(Looper.getMainLooper()).post(() -> {
                    if (dataListener != null) {
                        dataListener.onError("读取线圈失败: " + errorMsg);
                    }
                });
            }
        }).start();
    }
    
    /**
     * 读取离散输入(Discrete Input)状态
     * @param address 起始地址 (0-65535)
     * @param count 读取数量 (1-2000)
     */
    public void readDiscreteInputs(final int address, final int count) {
        if (!isConnected()) {
            if (dataListener != null) {
                dataListener.onError("未连接到串口设备");
            }
            return;
        }
        
        // 创建后台线程发送请求
        new Thread(() -> {
            try {
                // 创建读取离散输入请求
                ReadInputDiscretesRequest request = new ReadInputDiscretesRequest(address, count);
                request.setUnitID(unitId);
                
                // 执行请求
                transaction.setRequest(request);
                transaction.execute();
                
                // 获取响应
                ReadInputDiscretesResponse response = (ReadInputDiscretesResponse) transaction.getResponse();
                
                // 解析结果
                BitVector bitVector = response.getDiscretes();
                boolean[] values = new boolean[count];
                for (int i = 0; i < count; i++) {
                    values[i] = bitVector.getBit(i);
                }
                
                // 在主线程通知结果
                final boolean[] finalValues = values;
                new Handler(Looper.getMainLooper()).post(() -> {
                    if (dataListener != null) {
                        dataListener.onDataReceived(finalValues, "discrete_input", String.valueOf(address));
                    }
                });
            } catch (Exception e) {
                Log.e(TAG, "读取离散输入失败: " + e.getMessage(), e);
                final String errorMsg = e.getMessage();
                new Handler(Looper.getMainLooper()).post(() -> {
                    if (dataListener != null) {
                        dataListener.onError("读取离散输入失败: " + errorMsg);
                    }
                });
            }
        }).start();
    }
    
    /**
     * 读取保持寄存器(Holding Register)
     * @param address 起始地址 (0-65535)
     * @param count 读取数量 (1-125)
     */
    public void readHoldingRegisters(final int address, final int count) {
        if (!isConnected()) {
            if (dataListener != null) {
                dataListener.onError("未连接到串口设备");
            }
            return;
        }
        
        // 创建后台线程发送请求
        new Thread(() -> {
            try {
                // 创建读取保持寄存器请求
                ReadMultipleRegistersRequest request = new ReadMultipleRegistersRequest(address, count);
                request.setUnitID(unitId);
                
                // 执行请求
                transaction.setRequest(request);
                transaction.execute();
                
                // 获取响应
                ReadMultipleRegistersResponse response = (ReadMultipleRegistersResponse) transaction.getResponse();
                
                // 解析结果
                int[] values = new int[count];
                for (int i = 0; i < count; i++) {
                    values[i] = response.getRegisterValue(i);
                }
                
                // 在主线程通知结果
                final int[] finalValues = values;
                new Handler(Looper.getMainLooper()).post(() -> {
                    if (dataListener != null) {
                        dataListener.onDataReceived(finalValues, "holding_register", String.valueOf(address));
                    }
                });
            } catch (Exception e) {
                Log.e(TAG, "读取保持寄存器失败: " + e.getMessage(), e);
                final String errorMsg = e.getMessage();
                new Handler(Looper.getMainLooper()).post(() -> {
                    if (dataListener != null) {
                        dataListener.onError("读取保持寄存器失败: " + errorMsg);
                    }
                });
            }
        }).start();
    }
    
    /**
     * 读取输入寄存器(Input Register)
     * @param address 起始地址 (0-65535)
     * @param count 读取数量 (1-125)
     */
    public void readInputRegisters(final int address, final int count) {
        if (!isConnected()) {
            if (dataListener != null) {
                dataListener.onError("未连接到串口设备");
            }
            return;
        }
        
        // 创建后台线程发送请求
        new Thread(() -> {
            try {
                // 创建读取输入寄存器请求
                ReadInputRegistersRequest request = new ReadInputRegistersRequest(address, count);
                request.setUnitID(unitId);
                
                // 执行请求
                transaction.setRequest(request);
                transaction.execute();
                
                // 获取响应
                ReadInputRegistersResponse response = (ReadInputRegistersResponse) transaction.getResponse();
                
                // 解析结果
                int[] values = new int[count];
                for (int i = 0; i < count; i++) {
                    values[i] = response.getRegisterValue(i);
                }
                
                // 在主线程通知结果
                final int[] finalValues = values;
                new Handler(Looper.getMainLooper()).post(() -> {
                    if (dataListener != null) {
                        dataListener.onDataReceived(finalValues, "input_register", String.valueOf(address));
                    }
                });
            } catch (Exception e) {
                Log.e(TAG, "读取输入寄存器失败: " + e.getMessage(), e);
                final String errorMsg = e.getMessage();
                new Handler(Looper.getMainLooper()).post(() -> {
                    if (dataListener != null) {
                        dataListener.onError("读取输入寄存器失败: " + errorMsg);
                    }
                });
            }
        }).start();
    }
    
    /**
     * 写入单个线圈(Coil)
     * @param address 地址 (0-65535)
     * @param value 写入值 (true/false)
     */
    public void writeSingleCoil(final int address, final boolean value) {
        if (!isConnected()) {
            if (dataListener != null) {
                dataListener.onError("未连接到串口设备");
            }
            return;
        }
        
        // 创建后台线程发送请求
        new Thread(() -> {
            try {
                // 创建写入线圈请求
                WriteCoilRequest request = new WriteCoilRequest(address, value);
                request.setUnitID(unitId);
                
                // 执行请求
                transaction.setRequest(request);
                transaction.execute();
                
                // 获取响应并验证
                ModbusResponse response = transaction.getResponse();
                if (response != null) {
                    // 在主线程通知结果
                    new Handler(Looper.getMainLooper()).post(() -> {
                        if (dataListener != null) {
                            dataListener.onDataReceived(value, "coil_write", String.valueOf(address));
                        }
                    });
                } else {
                    throw new ModbusException("写入线圈无响应");
                }
            } catch (Exception e) {
                Log.e(TAG, "写入线圈失败: " + e.getMessage(), e);
                final String errorMsg = e.getMessage();
                new Handler(Looper.getMainLooper()).post(() -> {
                    if (dataListener != null) {
                        dataListener.onError("写入线圈失败: " + errorMsg);
                    }
                });
            }
        }).start();
    }
    
    /**
     * 写入单个寄存器(Register)
     * @param address 地址 (0-65535)
     * @param value 写入值 (0-65535)
     */
    public void writeSingleRegister(final int address, final int value) {
        if (!isConnected()) {
            if (dataListener != null) {
                dataListener.onError("未连接到串口设备");
            }
            return;
        }
        
        // 创建后台线程发送请求
        new Thread(() -> {
            try {
                // 创建写入寄存器请求
                WriteSingleRegisterRequest request = new WriteSingleRegisterRequest(address, new SimpleRegister(value));
                request.setUnitID(unitId);
                
                // 执行请求
                transaction.setRequest(request);
                transaction.execute();
                
                // 获取响应并验证
                ModbusResponse response = transaction.getResponse();
                if (response != null) {
                    // 在主线程通知结果
                    final int finalValue = value;
                    new Handler(Looper.getMainLooper()).post(() -> {
                        if (dataListener != null) {
                            dataListener.onDataReceived(finalValue, "register_write", String.valueOf(address));
                        }
                    });
                } else {
                    throw new ModbusException("写入寄存器无响应");
                }
            } catch (Exception e) {
                Log.e(TAG, "写入寄存器失败: " + e.getMessage(), e);
                final String errorMsg = e.getMessage();
                new Handler(Looper.getMainLooper()).post(() -> {
                    if (dataListener != null) {
                        dataListener.onError("写入寄存器失败: " + errorMsg);
                    }
                });
            }
        }).start();
    }
    
    /**
     * 写入多个寄存器
     * @param address 起始地址
     * @param values 值数组
     */
    public void writeMultipleRegisters(final int address, final int[] values) {
        if (!isConnected()) {
            if (dataListener != null) {
                dataListener.onError("未连接到串口设备");
            }
            return;
        }
        
        // 创建后台线程发送请求
        new Thread(() -> {
            try {
                // 创建寄存器数组
                SimpleRegister[] registers = new SimpleRegister[values.length];
                for (int i = 0; i < values.length; i++) {
                    registers[i] = new SimpleRegister(values[i]);
                }
                
                // 创建写入多个寄存器请求
                WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(address, registers);
                request.setUnitID(unitId);
                
                // 执行请求
                transaction.setRequest(request);
                transaction.execute();
                
                // 获取响应并验证
                ModbusResponse response = transaction.getResponse();
                if (response != null) {
                    // 在主线程通知结果
                    final int[] finalValues = values;
                    new Handler(Looper.getMainLooper()).post(() -> {
                        if (dataListener != null) {
                            dataListener.onDataReceived(finalValues, "registers_write", String.valueOf(address));
                        }
                    });
                } else {
                    throw new ModbusException("写入多个寄存器无响应");
                }
            } catch (Exception e) {
                Log.e(TAG, "写入多个寄存器失败: " + e.getMessage(), e);
                final String errorMsg = e.getMessage();
                new Handler(Looper.getMainLooper()).post(() -> {
                    if (dataListener != null) {
                        dataListener.onError("写入多个寄存器失败: " + errorMsg);
                    }
                });
            }
        }).start();
    }
    
    /**
     * 写入多个线圈
     * @param address 起始地址
     * @param values 值数组
     */
    public void writeMultipleCoils(final int address, final boolean[] values) {
        if (!isConnected()) {
            if (dataListener != null) {
                dataListener.onError("未连接到串口设备");
            }
            return;
        }
        
        // 创建后台线程发送请求
        new Thread(() -> {
            try {
                // 创建位向量
                BitVector bitVector = new BitVector(values.length);
                for (int i = 0; i < values.length; i++) {
                    bitVector.setBit(i, values[i]);
                }
                
                // 创建写入多个线圈请求
                WriteMultipleCoilsRequest request = new WriteMultipleCoilsRequest(address, bitVector);
                request.setUnitID(unitId);
                
                // 执行请求
                transaction.setRequest(request);
                transaction.execute();
                
                // 获取响应并验证
                ModbusResponse response = transaction.getResponse();
                if (response != null) {
                    // 在主线程通知结果
                    final boolean[] finalValues = values;
                    new Handler(Looper.getMainLooper()).post(() -> {
                        if (dataListener != null) {
                            dataListener.onDataReceived(finalValues, "coils_write", String.valueOf(address));
                        }
                    });
                } else {
                    throw new ModbusException("写入多个线圈无响应");
                }
            } catch (Exception e) {
                Log.e(TAG, "写入多个线圈失败: " + e.getMessage(), e);
                final String errorMsg = e.getMessage();
                new Handler(Looper.getMainLooper()).post(() -> {
                    if (dataListener != null) {
                        dataListener.onError("写入多个线圈失败: " + errorMsg);
                    }
                });
            }
        }).start();
    }
    
    /**
     * 执行通用的Modbus请求
     * @param request Modbus请求
     * @return Modbus响应
     * @throws ModbusException 通信异常
     */
    public ModbusResponse executeRequest(ModbusRequest request) throws ModbusException {
        if (!isConnected()) {
            throw new ModbusException("未连接到串口设备");
        }
        
        // 设置UnitID
        request.setUnitID(unitId);
        
        // 执行请求
        transaction.setRequest(request);
        transaction.execute();
        
        // 返回响应
        return transaction.getResponse();
    }
} 